/*Trabalho1 - Programação Concorrente - Grupo04 Turma B
	Daniel Rodrigues Hatanaka - 7277579
	Guilherme Elias Guimarães Kalatzis Sousa – 7239426
	Thiago Fujisaka Tanaka - 7153014
Basta compilar o main.c que ele está associado com os arquivos .h necessários

LINUX:

gcc -pthread main.c -lgmp

MAC OS:

gcc main.c -lgmp

*/
#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <pthread.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>

struct timeval inicio,final;

pthread_mutex_t mutex;
pthread_t thread_1, thread_2, thread_3;
mpf_t a0;
mpf_t b0;
mpf_t t0;
mpf_t p0;
mpf_t a1;
mpf_t b1;
mpf_t t1;
mpf_t p1;
mpf_t pi;
mpf_t temp1;
mpf_t temp2;
mpf_t piAnterior;

void *calculaAeTparalelo(){

        pthread_mutex_lock(&mutex);
				//Calcula a1
				mpf_add(a1,a0,b0);
                mpf_div_ui(a1,a1,2);
				//Calcula t1
                mpf_sub (temp1, a0, a1);
    			mpf_pow_ui (temp2,temp1,2);
    			mpf_mul (temp1,p0,temp2);
    			mpf_sub (t1, t0, temp1);

        pthread_mutex_unlock(&mutex);
        pthread_exit(NULL);
}

void *calculaBparalelo(){

        pthread_mutex_lock(&mutex);
        //Calcula b1
                mpf_mul(b1,a0,b0);
                mpf_sqrt(b1,b1);
        pthread_mutex_unlock(&mutex);
        pthread_exit(NULL);
}

void *calculaPparalelo(){

              //Calcula o p1
                mpf_mul_ui(p1, p0, 2);
                pthread_exit(NULL);
}

void destrutorParalelo(){

    mpf_clear(a0);
    mpf_clear(b0);
    mpf_clear(t0);
    mpf_clear(p0);
    mpf_clear(a1);
    mpf_clear(b1);
    mpf_clear(t1);
    mpf_clear(p1);
    mpf_clear(pi);
    mpf_clear(temp1);
    mpf_clear(temp2);
    mpf_clear(piAnterior);
}

void GaussLegendreParalelo(){
    	FILE *GaussLegendrePar;
    	GaussLegendrePar = fopen("gaussLegendreParalelo.txt", "w+");
        int time;

        mpf_set_default_prec(34000000);

        unsigned int cont=0;

    	mpf_init(a0);
    	mpf_init(b0);
    	mpf_init(t0);
    	mpf_init(p0);
    	mpf_init(a1);
    	mpf_init(b1);
    	mpf_init(t1);
    	mpf_init(p1);
    	mpf_init(pi);
    	mpf_init(temp1);
    	mpf_init(temp2);
    	mpf_init(piAnterior);

    	gettimeofday(&inicio, NULL);
    	//Iniciando a0
    	mpf_set_d (a0,1);
 		//Iniciando b0

 		mpf_sqrt_ui(temp1,2);
    	mpf_ui_div(b0, 1, temp1);
    	//Iniciando t0
		mpf_div_ui(t0,a0,4);

    	//Iniciando p0
    	mpf_set_d (p0,1);

		for(;;){

                pthread_create(&thread_1,NULL,calculaAeTparalelo,NULL);
                pthread_create(&thread_2,NULL,calculaBparalelo,NULL);
                pthread_create(&thread_3,NULL,calculaPparalelo,NULL);

                pthread_join(thread_1,NULL);
                pthread_join(thread_2,NULL);
                pthread_join(thread_3,NULL);

                //Calculo de pi

                mpf_add(pi,a1,b1);
    			mpf_pow_ui (pi,pi,2);
    			mpf_mul_ui(temp1,t1,4);
    			mpf_div (pi,pi,temp1);


                mpf_swap(a0,a1);
                mpf_swap(b0,b1);
                mpf_swap(t0,t1);
                mpf_swap(p0,p1);

                if(mpf_eq(pi, piAnterior, 34000000)) {
    				gmp_printf("pi calculado = %.10000000Ff\n",pi);
					mpf_out_str(GaussLegendrePar,10,10000000,pi);
				break;
				}
    			mpf_swap (piAnterior,pi);
                cont++;

        }
        gettimeofday(&final, NULL);
		time = final.tv_sec - inicio.tv_sec;
    	printf("Contador de iterações %i\n\n", cont);
    	printf("Tempo em %i segundos\n",time);

        destrutorParalelo();

}
